/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年11月7日
 */
package com.massyframework.beanskin.runtime.service.scriptengine;

import java.io.ByteArrayInputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.LoaderClassPath;

/**
 * 脚本引擎增强器,对graaljs内核进行代码增强，去掉对并发检测的代码段.
 * @author huangkh
 *
 */
class ScriptEngineTransformer implements ClassFileTransformer {

	private String name = "com/oracle/truffle/polyglot/PolyglotContextImpl";
	private boolean transformed = false;

	/**
	 * 
	 */
	public ScriptEngineTransformer() {
		
	}
	
	public boolean retransform(Instrumentation inst) {
		inst.addTransformer(this);
		try {
			Class<?> clazz =  this.getClass().getClassLoader().loadClass("com.oracle.truffle.polyglot.PolyglotContextImpl");
			if (this.transformed == true) {
				return true;
			}
			
			if (inst.isModifiableClass(clazz)) {
				inst.retransformClasses(clazz);
			}
						
			return this.transformed ;
		}catch(Exception e) {
			return false;
		}finally {
			inst.removeTransformer(this);
		}
	}

	@Override
	public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
			ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
		if (className.equals(name)) {
			try {
				ClassPool pool = ClassPool.getDefault();
				if (loader != null) {
					pool.insertClassPath(new LoaderClassPath(loader));
				}
				CtClass clazz = pool.makeClass(new ByteArrayInputStream(classfileBuffer));
				this.transformed = true;
				return this.transform(clazz, pool);
			}catch(Exception e) {
				Logger logger = LoggerFactory.getLogger(ScriptEngineTransformer.class);
				if (logger.isErrorEnabled()) {
					logger.error("cannot instrumatation graaljs.", e);
				}
			}
		}
		return null;
	}

	protected byte[] transform(CtClass clazz, ClassPool pool) throws Exception {
		CtClass threadType = pool.getCtClass(Thread.class.getName());
		CtClass boolType = CtClass.booleanType;
		CtMethod method = clazz.getDeclaredMethod("checkAllThreadAccesses", new CtClass[] {threadType, boolType});
		method.setBody("{}");
		
		StringBuilder builder = new StringBuilder();
		builder.append("{\n" + 
				"        com.oracle.truffle.polyglot.PolyglotThreadInfo threadInfo = new com.oracle.truffle.polyglot.PolyglotThreadInfo($0, $1);\n" + 
				"        singleThreadValue.update(threadInfo);\n" + 
				"        return threadInfo;\n" + 
				"    }");
		
		method = clazz.getDeclaredMethod("createThreadInfo", new CtClass[] {threadType});
		method.setBody(builder.toString());
		
		return clazz.toBytecode();
	}
}
